﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace PControl
{
    /// <summary>
    /// 温度显示控件
    /// </summary>
    public partial class Temperature : UserControl
    {
        /// <summary>
        /// 最大温度
        /// </summary>
        private Decimal maxTemp = 100;
        /// <summary>
        /// 最小温度
        /// </summary>
        private Decimal miniTemp = 0;
        /// <summary>
        /// 当前温度
        /// </summary>
        private Decimal currentTemp = 0;
        /// <summary>
        /// 控件背景颜色
        /// </summary>
        private Color backColor;
        /// <summary>
        /// 温度显示颜色
        /// </summary>
        private Color tempColor = Color.Red;
        /// <summary>
        /// 控件标题显示颜色
        /// </summary>
        private Color textColor = Color.Black;
        /// <summary>
        /// 控件周边显示颜色
        /// </summary>
        private Color borderColor=Color.Gray;
        /// <summary>
        /// 控件文字位置，上或下
        /// </summary>
        private Styles textStyle = Styles.TextUp;
        /// <summary>
        /// 控件最大尺寸
        /// </summary>
        private Size maximumSize = new Size(150, 900);
        /// <summary>
        /// 控件最小尺寸
        /// </summary>
        private Size minimumsize = new Size(60, 120);
        /// <summary>
        /// 控件尺寸修改前尺寸
        /// </summary>
        private Size preSize;

        /// <summary>
        /// 获取或设置控件最大显示温度值
        /// </summary>
        [Description("获取或设置控件最大显示温度值")]
        public Decimal MaxTemp
        {
            get { return maxTemp; }
            set
            {
                maxTemp = value;
                if (maxTemp < miniTemp)
                    maxTemp = miniTemp;
                if (maxTemp < currentTemp)
                    maxTemp = currentTemp;
                tempFrame.Refresh();
                tempLeft.Refresh();
                tempRight.Refresh();
            }
        }
        /// <summary>
        /// 获取或设置控件最小显示温度值
        /// </summary>
        [Description("获取或设置控件最小显示温度值")]
        public Decimal MiniTemp
        {
            get { return miniTemp; }
            set
            {
                miniTemp = value;
                if (miniTemp > maxTemp)
                    miniTemp = maxTemp;
                if (miniTemp > currentTemp)
                    miniTemp = currentTemp;
                tempFrame.Refresh();
                tempLeft.Refresh();
                tempRight.Refresh();
            }
        }
        /// <summary>
        /// 获取或设置控件当前显示温度值
        /// </summary>
        [Description("获取或设置控件当前显示温度值")]
        public Decimal Value
        {
            get { return currentTemp; }
            set
            {
                currentTemp = value;
                if (currentTemp > maxTemp)
                    currentTemp = maxTemp;
                if (currentTemp < miniTemp)
                    currentTemp = miniTemp;
                tempFrame.Refresh();
            }
        }
        /// <summary>
        /// 获取或设置控件背景颜色
        /// </summary>
        [Description("获取或设置控件背景颜色")]
        public new Color BackColor
        {
            get { return backColor; }
            set
            {
                backColor = value;
                //degree.BackColor = value;
                //tempFrame.BackColor = value;
                //tempLeft.BackColor = value;
                //tempRight.BackColor = value;
            }
        }
        [Description("获取或设置控件周围背景颜色")]
        public Color BorderColor
        {
            get { return borderColor; }
            set
            {
                borderColor = value;
            }
        }
        [Browsable(false)]
        [Description("获取或设置控件温度显示颜色")]
        public Color TempColor
        {
            get { return tempColor; }
            set
            {
                tempColor = value;
                tempFrame.Refresh();
            }
        }
        [Description("获取或设置控件标题显示颜色")]
        public Color TextColor
        {
            get { return textColor; }
            set
            {
                textColor = value;
                degree.ForeColor = textColor;
            }
        }
        [Description("获取或设置控件标题样式")]
        public Styles TextStyle
        {
            get { return textStyle; }
            set
            {
                if (textStyle == value)
                    return;
                else
                {
                    textStyle = value;
                    if (textStyle == Styles.TextUp)
                    {
                        degree.Top = 0;
                        tempFrame.Top = tempLeft.Top = tempRight.Top = degree.Height;
                    }
                    else
                    {
                        Point point = new Point(0, this.Height - degree.Height);
                        degree.Location = point;
                        point = new Point(0, 0);
                        tempFrame.Location = point;
                        tempLeft.Location = point;
                        point = new Point(this.Width - tempLeft.Width, 0);
                        tempRight.Location = point;
                    }
                }
            }
        }
        [Description("获取或设置控件最大尺寸")]
        public new Size MaximumSize
        {
            get { return this.maximumSize; }
            set
            {
                base.MaximumSize = this.maximumSize = value; ;
            }
        }
        [Description("获取或设置控件最小尺寸")]
        public new Size MinimumSize
        {
            get { return this.minimumsize; }
            set
            {
                base.MinimumSize = this.minimumsize = value;
            }
        }
        /// <summary>
        /// 默认构造方法
        /// </summary>
        public Temperature()
        {
            InitializeComponent();
            //设置图元刷新时双缓冲，貌似不起作用
            //this.SetStyle(ControlStyles.OptimizedDoubleBuffer |
            //        ControlStyles.ResizeRedraw |
            //        ControlStyles.AllPaintingInWmPaint, true);
            //this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.DoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
            //this.UpdateStyles();
            base.MaximumSize = maximumSize;
            base.MinimumSize = minimumsize;
            preSize = this.Size;
        }
        /// <summary>
        /// 控件大小改变事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnSizeChanged(object sender, EventArgs e)
        {
            if (this.Size != preSize)
            {
                sizeChanged();
                preSize = this.Size;
            }
        }
        /// <summary>
        /// 控件大小修改处理方法
        /// </summary>
        private void sizeChanged()
        {
            int width3 = this.Width / 3;
            tempFrame.Width = this.Width;
            tempLeft.Width = tempRight.Width = width3;
            tempRight.Left = this.Width- width3;
            int textHeight = 30;
            float textFont = 10.5F;
            if (this.Height < 200)
            {
                textHeight = 20;
                textFont = 9.0F;
            }
            else
            {
                textHeight = 30;
            }
            degree.Font = new Font("宋体", textFont, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
            tempLeft.Height = tempRight.Height = this.Height - (int)this.Width * 4 / 15 - textHeight;
            tempFrame.Height = this.Height - textHeight;
            degree.Height = textHeight;
            degree.Width = this.Width;
            if (textStyle == Styles.TextUp)
            {
                degree.Top = 0;
                tempLeft.Top = tempRight.Top = tempFrame.Top = textHeight;
            }
            else
            {
                degree.Top = this.Height - textHeight;
                tempLeft.Top = tempRight.Top = tempFrame.Top = 0;
            }

            tempLeft.Refresh();
            tempRight.Refresh();
            tempFrame.Refresh();
            degree.Refresh();
        }

        #region 绘制圆角
        public void SetWindowRegion()
        {
            System.Drawing.Drawing2D.GraphicsPath FormPath;
            FormPath = new System.Drawing.Drawing2D.GraphicsPath();
            Rectangle rect = new Rectangle(1, 1, this.Width - 2, this.Height - 2);//this.Left-10,this.Top-10,this.Width-10,this.Height-10);                 
            FormPath = GetRoundedRectPath(rect);
            this.Region = new Region(FormPath);
        }

        private GraphicsPath GetRoundedRectPath(Rectangle rect)
        {
            int radius=5;
            Rectangle arcRect = new Rectangle(rect.Location, new Size(radius*2, radius*2));
            GraphicsPath path = new GraphicsPath();
            //   左上角   
            path.AddArc(arcRect, 180, 90);
            //   右上角   
            arcRect.X = rect.Right - radius*2;
            path.AddArc(arcRect, 270, 90);
            //   右下角   
            arcRect.Y = rect.Bottom - radius*2;
            path.AddArc(arcRect, 0, 90);
            //   左下角   
            arcRect.X = rect.Left;
            path.AddArc(arcRect, 90, 90);
            return path;
        }
        #endregion

        #region 
        public void DrawRoundRectangle(Graphics g, Rectangle rectangle, float borderWidth)
        {
            // 如要使边缘平滑，请取消下行的注释   
            g.SmoothingMode = SmoothingMode.HighQuality;   

            // 由于边框也需要一定宽度，需要对矩形进行修正   
            rectangle = new Rectangle(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
            Pen p = new Pen(borderColor, borderWidth);
            // 调用 getRoundRectangle 得到圆角矩形的路径，然后再进行绘制   
            g.DrawPath(p, getRoundRectangle(rectangle));
        }

        /// <summary>   
        /// 根据普通矩形得到圆角矩形的路径   
        /// </summary>   
        /// <param name="rectangle">原始矩形</param>   
        /// <param name="r">半径</param>   
        /// <returns>图形路径</returns>   
        private GraphicsPath getRoundRectangle(Rectangle rectangle)
        {
            int radius = 5;
            int l = 2 * radius;
            // 把圆角矩形分成八段直线、弧的组合，依次加到路径中   
            GraphicsPath gp = new GraphicsPath();
            gp.AddLine(new Point(rectangle.X + radius, rectangle.Y), new Point(rectangle.Right - radius, rectangle.Y));
            gp.AddArc(new Rectangle(rectangle.Right - l, rectangle.Y, l, l), 270F, 90F);

            gp.AddLine(new Point(rectangle.Right, rectangle.Y + radius), new Point(rectangle.Right, rectangle.Bottom - radius));
            gp.AddArc(new Rectangle(rectangle.Right - l, rectangle.Bottom - l, l, l), 0F, 90F);

            gp.AddLine(new Point(rectangle.Right - radius, rectangle.Bottom), new Point(rectangle.X + radius, rectangle.Bottom));
            gp.AddArc(new Rectangle(rectangle.X, rectangle.Bottom - l, l, l), 90F, 90F);

            gp.AddLine(new Point(rectangle.X, rectangle.Bottom - radius), new Point(rectangle.X, rectangle.Y + radius));
            gp.AddArc(new Rectangle(rectangle.X, rectangle.Y, l, l), 180F, 90F);
            return gp;
        }  
        #endregion

        /// <summary>
        /// 重载控件重绘事件
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {
            //Graphics graphics = e.Graphics;
            //base.OnPaint(e);
            //sizeChanged();
            DrawRoundRectangle(e.Graphics, new Rectangle(1, 1, this.Width-2,this.Height-2), 4.0f);
            SetWindowRegion();
        }
        /// <summary>
        /// 绘制控件温度显示区
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void temp_Paint(object sender, PaintEventArgs e)
        {
            Graphics graphics = e.Graphics;
            drawDegree(graphics, e);
        }
        /// <summary>
        /// 绘制控件刻度
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void px_Paint(object sender, PaintEventArgs e)
        {
            if ((PictureBox)sender == tempLeft)
            {
                //画左边标尺
                drawScaleplate(1, e);
            }
            else if ((PictureBox)sender == tempRight)
            {
                //画右边标尺
                drawScaleplate(2, e);
            }
        }

        public void drawDegree(Graphics graphics, PaintEventArgs e)
        {
            #region 调整数据精度
            decimal diffDegree = maxTemp - miniTemp;
            int fraction = calFraction(diffDegree);
            currentTemp = Math.Round(currentTemp, fraction);
            maxTemp = Math.Round(maxTemp, fraction);
            miniTemp = Math.Round(miniTemp, fraction);
            #endregion

            #region 数据初始化
            if ((miniTemp == maxTemp))
                return;

            int tempWidth = tempFrame.Width/3;                                  // the bar area width
            int width10 = tempWidth / 10;                                       // the bar bargin width
            int width = tempWidth - 2 * width10;                                // the bar width
            int height;		                                                    // the bar height
            int radiuTop = width / 2;
            int radiuBottom = tempWidth/2;
            int deltaTop = radiuTop / 4;
            int deltaBottom = radiuBottom * 2 / 5;
            int tempHeight = tempLeft.Height;
            int linesnum = tempHeight / 50;                 //刻度数
            int pxHeight = (tempHeight - radiuTop-radiuBottom-deltaTop) / linesnum;       //相邻刻度高度差
            tempHeight = pxHeight * linesnum;//温度实际显示区高度因整数相除取整去余产生的误差，需进行修正处理
            height = (int)((currentTemp - miniTemp) * (decimal)tempHeight / (maxTemp - miniTemp));
            deltaBottom = tempFrame.Height - radiuTop - radiuBottom - deltaTop - height;
            int width4 = width / 4;
            int x = width10 + tempLeft.Width;	    // 温度区起点x
            int y = tempHeight-height + radiuTop + deltaTop;   // 温度区起点y
            Rectangle rect;
            Color color;
            LinearGradientBrush brush;
            graphics.SmoothingMode = SmoothingMode.AntiAlias;                               //消除锯齿
            graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;                         //高像素偏移质量
            graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;   //文字消除锯齿
            
            #region 检测数据是否正确
            //Console.WriteLine("tempWidth=" + tempWidth);
            //Console.WriteLine("width10=" + width10);
            //Console.WriteLine("width=" + width);
            //Console.WriteLine("height=" + height);
            //Console.WriteLine("x=" + x);
            //Console.WriteLine("y=" + y);
            //Console.WriteLine("width4=" + width4);
            #endregion
            #endregion

            #region 画非温度区
            Color[] colors = null;

            #region 绘制非温度区上边半球
            int Radiu = width / 2;
            color = Color.FromArgb(231, 238, 248);
            Color nuntempColor = Color.FromArgb(176, 201, 231);
            colors = new Color[] { nuntempColor, color, nuntempColor };
            float[] position = { 0.0f, 0.1f, 1.0f };
            ColorBlend blend = new ColorBlend();
            blend.Colors = colors;
            blend.Positions = position;
            rect = new Rectangle(x, 0, width, width);
            brush = new LinearGradientBrush(rect, nuntempColor, nuntempColor, 0, false);
            brush.InterpolationColors = blend;
            graphics.FillEllipse(brush, rect);
            #endregion

            if (y != radiuTop)
            {
                #region 画非温度区左半部分
                rect = new Rectangle(x, radiuTop, width4, y - radiuTop);
                brush = new LinearGradientBrush(rect, nuntempColor, color, 0, false);
                graphics.FillRectangle(brush, rect);
                #endregion

                #region 画非温度区右半部分
                rect = new Rectangle(width4 + x, radiuTop, width - width4, y - radiuTop);
                brush = new LinearGradientBrush(rect, nuntempColor, color, 180, false);
                graphics.FillRectangle(brush, rect);
                #endregion
            }
            #endregion

            #region 画温度区
            if (tempFrame.Height != y + radiuBottom)
            {
                #region 画温度区左半部分
                color = Color.FromArgb(tempColor.R, tempColor.G + 100, tempColor.B + 100);
                rect = new Rectangle(x, y, width4, tempFrame.Height-y-radiuBottom);
                brush = new LinearGradientBrush(rect, tempColor, color, 0, false);
                graphics.FillRectangle(brush, rect);
                #endregion

                #region 画温度区右半部分
                rect = new Rectangle(width4 + x, y, width - width4, tempFrame.Height - y - radiuBottom);
                brush = new LinearGradientBrush(rect, tempColor, color, 180, false);
                graphics.FillRectangle(brush, rect);
                #endregion
            }
            #endregion

            #region 更新数据
            x = tempFrame.Width / 2;              //底部圆心x
            y = tempFrame.Height - radiuBottom;   //底部圆心y
            //Console.WriteLine("tempLeft.Width=" + tempLeft.Width);
            //Console.WriteLine("tempLeft.Height=" + tempLeft.Height);
            //Console.WriteLine("tempFrame.Width=" + tempFrame.Width);
            //Console.WriteLine("tempFrame.Height=" + tempFrame.Height);
            //Console.WriteLine("this.Width=" + this.Width);
            //Console.WriteLine("this.Height=" + this.Height);
            //Console.WriteLine("x=" + x);
            //Console.WriteLine("y=" + y);
            #endregion

            #region 画温度区底部圆区域
            GraphicsPath path = new GraphicsPath();
            rect = new Rectangle(x - radiuBottom, y - radiuBottom, radiuBottom * 2, radiuBottom * 2);
            path.AddEllipse(rect);
            PathGradientBrush pthGrBrush = new PathGradientBrush(path);
            pthGrBrush.CenterColor = Color.FromArgb(255, tempColor.R, tempColor.G + 100, tempColor.B + 100);
            colors = new Color[]{tempColor};
            pthGrBrush.SurroundColors = colors;
            graphics.FillRectangle(new SolidBrush(Color.FromArgb(0, 0, 0, 0)), e.ClipRectangle);
            graphics.FillEllipse(pthGrBrush, rect);
            #endregion

            #region 度数标记
            degree.Text = String.Format("{0}℃", currentTemp);
            #endregion
        }

        #region 改变文本显示位置
        private void changeStyle()
        {
            if (textStyle == Styles.TextUp)
            {
                if (degree.Top < 5)
                    return;
                else
                {
                    degree.Top = 0;
                    tempFrame.Top=tempLeft.Top=tempRight.Top = degree.Height;
                    degree.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
                }
            }
            else
            {
                if (Math.Abs(degree.Top - tempFrame.Height) < 5)
                    return;
                else
                {
                    tempFrame.Top = tempLeft.Top = tempRight.Top = 0;
                    degree.Top = tempFrame.Height;
                    degree.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
                }
            }
            this.Refresh();
        }
        #endregion

        //画刻度盘
        public void drawScaleplate(int flag, PaintEventArgs e)
        {
            #region 调整数据精度
            decimal diffDegree = maxTemp - miniTemp;
            int fraction = calFraction(diffDegree);
            currentTemp = Math.Round(currentTemp, fraction);
            maxTemp = Math.Round(maxTemp, fraction);
            miniTemp = Math.Round(miniTemp, fraction);
            #endregion

            #region 数据初始化
            if ((miniTemp == maxTemp))
                return;
            Graphics graphics = e.Graphics;
            int tempWidth = tempFrame.Width / 3;                                  // the bar area width
            int width10 = tempWidth / 10;
            int width = tempWidth - 2 * width10;                                // the bar width
            int width4 = width / 4;
            int radiuTop = width / 2;
            int radiuBottom = tempWidth /2;
            int deltaTop = radiuTop / 4;
            int deltaBottom = radiuBottom * 2 / 5;
            int tempHeight = tempLeft.Height;
            int linesnum = tempHeight / 50;                 //刻度数
            float pxHeight = (tempHeight - radiuTop - radiuBottom - deltaTop) / linesnum;       //相邻刻度高度差
            tempHeight = (int)(pxHeight * linesnum);//温度实际显示区高度因整数相除取整去余产生的误差，需进行修正处理
            decimal deltaDegree = diffDegree / (decimal)linesnum;//相邻刻度值之差
            int pxFrameWidth = tempRight.Width;         //刻度显示区宽度
            float pxHeight10 = pxHeight / 10.0f;        //相邻小刻度高度差
            float currentLocation = tempHeight + radiuTop + deltaTop;  //当前刻度所在坐标值
            String showText = "";                         //显示刻度值
            SizeF sizeF;
            graphics.SmoothingMode = SmoothingMode.AntiAlias;                               //消除锯齿
            graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;                         //高像素偏移质量
            graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;   //文字消除锯齿

            #endregion

            using (SolidBrush brushString = new SolidBrush(this.ForeColor))
            using (StringFormat format = new StringFormat())
            using (Font fontThink = new Font("宋体", 8))
            using (Pen penThick = new Pen(this.ForeColor, 2f))
            using (Pen penThin = new Pen(this.ForeColor, 1f))
            {
                #region 绘制一级刻度
                if (flag == 1)
                {
                    for (int i = 0; i < linesnum+1; i++)
                    {
                        graphics.DrawLine(penThick, tempLeft.Width - 15, currentLocation, tempLeft.Width, currentLocation);

                        #region 绘制刻度值
                        showText = "" + Math.Round((miniTemp + deltaDegree * i), fraction);
                        sizeF = graphics.MeasureString(showText, fontThink);
                        graphics.DrawString(showText, fontThink, brushString, 0, currentLocation - sizeF.Height + 1, format);
                        #endregion
                        #region 绘制二级刻度
                        if (i != linesnum)
                        {
                            for (int j = 1; j < 10; j++)
                            {
                                float tempLocation = currentLocation - pxHeight10 * j;
                                if (j == 5)
                                {
                                    graphics.DrawLine(penThin, tempLeft.Width - 12, tempLocation, tempLeft.Width, tempLocation);
                                }
                                else
                                {
                                    graphics.DrawLine(penThin, tempLeft.Width - 7, tempLocation, tempLeft.Width, tempLocation);
                                }
                            }
                        }
                        #endregion
                        currentLocation -= pxHeight;
                    }
                }
                else if(flag==2)
                {
                    for (int i = 0; i < linesnum+1 ; i++)
                    {
                        graphics.DrawLine(penThick, 0, currentLocation, 15, currentLocation);
                        #region 绘制刻度值
                        showText = "" + Math.Round((miniTemp + deltaDegree * i), fraction);
                        sizeF = graphics.MeasureString(showText, fontThink);
                        graphics.DrawString(showText, fontThink, brushString, pxFrameWidth - sizeF.Width, currentLocation - sizeF.Height + 1, format);
                        #endregion
                        if (i != linesnum)
                        {
                            #region 绘制二级刻度
                            for (int j = 1; j < 10; j++)
                            {
                                float tempLocation = currentLocation - pxHeight10 * j;
                                if (j == 5)
                                {
                                    graphics.DrawLine(penThin, 0, tempLocation, 12, tempLocation);
                                }
                                else
                                {
                                    graphics.DrawLine(penThin, 0, tempLocation, 7, tempLocation);
                                }
                            }
                            #endregion
                        }
                        currentLocation -= pxHeight;
                    }
                }
                #endregion
            }
        }

        //获取温度显示小数位
        public int calFraction(decimal diffDegree)
        {
            int fraction = 1;
            if (diffDegree >= 50)
                fraction = 0;
            else if (diffDegree >= 5)
                fraction = 1;
            else if (diffDegree >= (decimal)0.5)
                fraction = 2;
            else if (diffDegree >= (decimal)0.05)
                fraction = 3;
            else if (diffDegree >= (decimal)0.005)
                fraction = 4;
            else fraction = 5;
            return fraction;
        }
    }

    public enum Styles
    {
        TextUp, TextDown
    }
}
